package com.example.maruko_course_backend.services.impl;

import com.example.maruko_course_backend.convertor.DisplayOrderConvertor;
import com.example.maruko_course_backend.convertor.GrammarHelper;
import com.example.maruko_course_backend.convertor.KnowledgeGrammarVoConvertor;
import com.example.maruko_course_backend.convertor.KnowledgeWordVoConvertor;
import com.example.maruko_course_backend.criterias.PageConditionCriteria;
import com.example.maruko_course_backend.criterias.PageCriteria;
import com.example.maruko_course_backend.criterias.SentenceCriteria;
import com.example.maruko_course_backend.criterias.SentenceKnowledgeCriteria;
import com.example.maruko_course_backend.dto.*;
import com.example.maruko_course_backend.entity.Grammar;
import com.example.maruko_course_backend.entity.NewWord;
import com.example.maruko_course_backend.entity.Sentence;
import com.example.maruko_course_backend.repositories.SentenceRepository;
import com.example.maruko_course_backend.services.SentenceService;
import com.example.maruko_course_backend.utils.ExampleUtil;
import com.example.maruko_course_backend.utils.ExcelUtil1;
import com.example.maruko_course_backend.utils.PageUtil;
import com.example.maruko_course_backend.vo.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import static com.example.maruko_course_backend.constants.CommonConstants.FAIL;
import static com.example.maruko_course_backend.constants.CommonConstants.SUCCESS;
import static com.example.maruko_course_backend.utils.PageUtil.createPageDtos;
import static com.example.maruko_course_backend.utils.StringUtil.removePointSuffixToInt;

@Service
public class SentenceServiceImpl implements SentenceService {
  public static final String CHAPTER_NUM = "chapterNum";
  public static final String UPLOAD_VIDEO_NUM = "upVideoNum";
  public static final String SENTENCE_SEQ = "sentenceSeq";
  public static final String SENTENCE_ORIGINAL_STR = "sentenceOriginalStr";
  
  @Autowired
  private SentenceRepository sentenceRepository;
  @Autowired
  private DisplayOrderConvertor displayOrderConvertor;
  @Autowired
  private KnowledgeWordVoConvertor knowledgeWordVoConvertor;
  @Autowired
  private KnowledgeGrammarVoConvertor knowledgeGrammarVoConvertor;
  @Autowired
  private GrammarHelper grammarHelper;
  
  @Override
  public Sentence saveSentence(Sentence sentence) {
    return sentenceRepository.save(sentence);
  }
  
  @Override
  public List<Map> getListByExcel(InputStream is, String fileName) {
    try {
      return (List<Map>) new ExcelUtil1(new UploadSentenceDto()).analysisExcel(is, fileName);
    } catch (Exception e) {
      e.printStackTrace();
    }
    return new ArrayList<>();
  }
  
  @Override
  public Boolean batchImportSentences(List<Map> uploadSentenceDtos) {
    try {
      for (int i = 1; i < uploadSentenceDtos.size(); i++) { //pass first head row
        Map row = uploadSentenceDtos.get(i);
        Sentence sentence = new Sentence();
        sentence.setChapterNum(removePointSuffixToInt((String) row.get(CHAPTER_NUM)));
        sentence.setUpVideoNum(removePointSuffixToInt((String) row.get(UPLOAD_VIDEO_NUM)));
        sentence.setSentenceSeq(removePointSuffixToInt((String) row.get(SENTENCE_SEQ)));
        sentence.setSentenceOriginalStr((String) row.get(SENTENCE_ORIGINAL_STR));
        saveSentence(sentence);
      }
    } catch (Exception e) {
      e.printStackTrace();
      return false;
    }
    return true;
  }
  
  @Override
  public SentenceKnowledgeVo findVoBySentenceKnowledgeCriteria(SentenceKnowledgeCriteria criteria) {
    Sentence sentence = findSentenceBySentenceCriteria(criteria);
    SentenceKnowledgeVo sentenceVo = convertByWordsAndGrammars(criteria, sentence.getNewWords(), sentence.getGrammars());
    sentenceVo.updateDisplayOrder(displayOrderConvertor.convert(sentenceVo.getWords(), sentenceVo.getGrammars(), sentence.getDisplayOrder()));
    return sentenceVo;
  }
  
  protected SentenceKnowledgeVo convertByWordsAndGrammars(SentenceKnowledgeCriteria criteria, List<NewWord> newWords, List<Grammar> grammars) {
    return SentenceKnowledgeVo.builder()
      .words(knowledgeWordVoConvertor.createWordsVoByEntity(newWords, criteria.getDisplayedTop10Words()))
      .grammars(knowledgeGrammarVoConvertor.createGrammarsVoByEntity(grammars, criteria.getDisplayedTop10Grammars()))
      .build();
  }
  
  @Override
  public Sentence findSentenceBySentenceCriteria(SentenceKnowledgeCriteria criteria) {
    List<Sentence> sentences = sentenceRepository.findAll(Example.of(ExampleUtil.createQueryExampleByCriteria(criteria)));
    return sentences.isEmpty() ? null : sentences.get(0);
  }
  
  @Override
  public PageDto<SentenceDto> findAllSentenceDtosByPage(PageCriteria pageCriteria) {
    Page<Sentence> pageSentences = sentenceRepository.findAll(PageUtil.createPageRequest(pageCriteria));
    return createPageDtos(pageSentences.getTotalElements(), pageSentences.getContent());
  }
  
  @Override
  public PageDto<SentenceDto> findAllSentenceDtosByPageCondition(PageConditionCriteria<SentenceCriteria> pageConditionCriteria) {
    Page<Sentence> pageResults = sentenceRepository.findAll(
      Example.of(
        ExampleUtil.createQueryExampleByCriteria(pageConditionCriteria.getDataCriteria()),
        Objects.requireNonNull(ExampleUtil.createExampleMatcher("sentenceOriginalStr", ExampleUtil.LIKE))
      ),
      PageUtil.createPageRequest(pageConditionCriteria.getPageCriteria())
    );
    return createPageDtos(pageResults.getTotalElements(), pageResults.getContent());
  }
  
  @Override
  public SentenceModifiedVo findSentenceModifiedVo(String videoNum, String sentenceSeq) {
    Sentence sentence = sentenceRepository.findAllByUpVideoNumAndSentenceSeq(Integer.valueOf(videoNum), Integer.valueOf(sentenceSeq));
    return createSentenceModifiedVoByEntity(sentence);
  }
  
  @Override
  public Sentence saveOrModifyWord(String videoNum, String sentenceSeq, EditedWord editedWord) {
    Sentence existedSentence = sentenceRepository.findAllByUpVideoNumAndSentenceSeq(Integer.valueOf(videoNum), Integer.valueOf(sentenceSeq));
    NewWord existedWord = findRelatedWord(editedWord.getContent(), existedSentence);
    if (existedWord == null) {
      saveNewWord(editedWord, existedSentence);
    } else {
      updateExistedWord(existedWord, editedWord);
    }
    return saveSentence(existedSentence);
  }
  
  private void updateExistedWord(NewWord existedWord, EditedWord editedWord) {
    existedWord.setContent(editedWord.getContent());
    existedWord.setPitch(editedWord.getPitch());
    existedWord.setMeaning(editedWord.getMeaning());
    existedWord.setCategory(editedWord.getCategory());
    existedWord.setPartOfSpeech(editedWord.getPartOfSpeech());
    existedWord.setIsSpecial(editedWord.getSpecial());
  }
  
  private void saveNewWord(EditedWord editedWord, Sentence existedSentence) {
    existedSentence.getNewWords().add(createNewWord(editedWord));
  }
  
  @Override
  public String deleteWord(String videoNum, String sentenceSeq, String editedWordContent) {
    try {
      Sentence existedSentence = sentenceRepository.findAllByUpVideoNumAndSentenceSeq(Integer.valueOf(videoNum), Integer.valueOf(sentenceSeq));
      NewWord relatedWord = findRelatedWord(editedWordContent, existedSentence);
      if (relatedWord == null) {
        return FAIL;
      }
      existedSentence.getNewWords().remove(relatedWord);
      sentenceRepository.save(existedSentence);
      return SUCCESS;
    } catch (Exception e) {
      e.printStackTrace();
      return FAIL;
    }
  }
  
  private NewWord findRelatedWord(String editedWordContent, Sentence existedSentence) {
    List<NewWord> existedWords = existedSentence.getNewWords().stream().filter(item -> item.getContent().equals(editedWordContent)).collect(Collectors.toList());
    return existedWords.isEmpty() ? null : existedWords.get(0);
  }
  
  private NewWord createNewWord(EditedWord editedWord) {
    NewWord result = new NewWord();
    result.setContent(editedWord.getContent());
    result.setCategory(editedWord.getCategory());
    result.setMeaning(editedWord.getMeaning());
    result.setPitch(editedWord.getPitch());
    result.setPartOfSpeech(editedWord.getPartOfSpeech());
    result.setIsSpecial(editedWord.getSpecial());
    return result;
  }
  
  private WordVo createWordVo(NewWord newWord) {
    WordVo wordVo = new WordVo();
    wordVo.setContent(newWord.getContent());
    wordVo.setPitch(newWord.getPitch());
    wordVo.setPartOfSpeech(newWord.getPartOfSpeech());
    wordVo.setMeaning(newWord.getMeaning());
    wordVo.setCategory(newWord.getCategory());
    wordVo.setSpecial(newWord.getIsSpecial());
    return wordVo;
  }
  
  private SentenceModifiedVo createSentenceModifiedVoByEntity(Sentence sentence) {
    SentenceModifiedVo result = new SentenceModifiedVo();
    result.setContent(sentence.getSentenceOriginalStr());
    result.setWordVos(createModifiedWordVos(sentence.getNewWords()));
    result.setGrammarVos(createModifiedGrammarVos(sentence.getGrammars()));
    return result;
  }
  
  private List<GrammarVo> createModifiedGrammarVos(List<Grammar> grammars) {
    List<GrammarCategoryDto> grammarCategoryDto = grammarHelper.createGrammarCategoryDto(grammars);
    return createGrammarVosByCategoryDto(grammarCategoryDto);
  }
  
  private List<GrammarVo> createGrammarVosByCategoryDto(List<GrammarCategoryDto> grammarCategoryDto) {
    List<GrammarVo> results = new ArrayList<>();
    for (GrammarCategoryDto categoryDto : grammarCategoryDto) {
      addToResultsByDiffType(results, categoryDto);
    }
    return results;
  }
  
  private void addToResultsByDiffType(List<GrammarVo> results, GrammarCategoryDto categoryDto) {
    if (grammarHelper.isCombinedTypeGrammarNeedSplit(categoryDto.getGrammars().size())) {
      results.addAll(generateSplitGrammarVos(categoryDto));
      return;
    }
    results.add(createGrammarVoByCategoryDto(categoryDto));
  }
  
  private List<GrammarVo> generateSplitGrammarVos(GrammarCategoryDto categoryDto) {
    List<GrammarVo> grammarVos = new ArrayList<>();
    for (List<Grammar> splitGrammar : grammarHelper.getSplitGrammars(categoryDto.getGrammars())) {
      grammarVos.add(createGrammarVo(categoryDto, splitGrammar));
    }
    return grammarVos;
  }
  
  private GrammarVo createGrammarVo(GrammarCategoryDto categoryDto, List<Grammar> splitGrammar) {
    return createGrammarVoByCategoryDto(GrammarCategoryDto.builder()
      .category(categoryDto.getCategory())
      .grammars(splitGrammar)
      .build()
    );
  }
  
  private GrammarVo createGrammarVoByCategoryDto(GrammarCategoryDto categoryDto) {
    GrammarVo grammarVo = new GrammarVo();
    grammarVo.setCategory(categoryDto.getCategory());
    grammarVo.setContentList(createGrammarContentList(categoryDto.getGrammars()));
    return grammarVo;
  }
  
  private List<GrammarContentVo> createGrammarContentList(List<Grammar> sameCategoryGrammar) {
    return sameCategoryGrammar.stream().map(item -> {
      GrammarContentVo grammarContentVo = new GrammarContentVo();
      grammarContentVo.setSeq(String.valueOf(item.getSeq()));
      grammarContentVo.setContent(item.getContent());
      return grammarContentVo;
    }).collect(Collectors.toList());
  }
  
  private List<WordVo> createModifiedWordVos(List<NewWord> newWords) {
    return newWords.stream().map(item -> createWordVo(item)).collect(Collectors.toList());
  }
}
